#ifndef __XVIDEO_UTIL_HPP__
#define __XVIDEO_UTIL_HPP__

#include <map>
#include <memory>
#include <functional>

#include "media.h"
#include "media_hapi.h"

extern "C" {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavutil/avutil.h>
}

//视频插帧
class XVInsertFrame {
public:
    typedef std::shared_ptr<XVInsertFrame> shared;
    typedef std::function<int(uint8_t* data, size_t size, int64_t pts, bool key_frame)> Output;
private:
    AVFrame* frame_ = nullptr;
    AVPacket* outpkt_ = nullptr;
    AVCodecContext* enctx_ = nullptr;
    int width_ = 0;
    int height_ = 0;
    uint8_t* buf_ = nullptr;
    std::vector<AVPacket*> gop_;
    int cur_frame_index_ = 0;

public:
    XVInsertFrame();
    ~XVInsertFrame();

    int init(int width, int height, int fps, int bit_rate, mediacodec_id_t codec);
    //强制编码器输出一个关键帧
    //输入的frame -> pict_type = AV_PICTURE_TYPE_I
    // frame -> key_frame = 1
    int getFrame(int64_t pts, bool key_frame, const Output& func);

    bool opened();
private:
    int encodeBlackPacket();
    AVFrame* buildBlackFrame();
    
};


//视频缓冲区
//以音频帧为基准
//音频帧不能有缺漏
class XVBuffer {
public:
    typedef std::function<int(uint8_t*, size_t size, int64_t pts)> Audio;
    typedef std::function<int(uint8_t*, size_t size, int64_t pts, bool key_frame)> Video;

    struct Listener {
        Audio audio = nullptr;
        Video video = nullptr;
    };

private:
    std::map<int64_t, std::vector<MediaFrame*>*>* buffer_ = nullptr;
    // std::map<int64_t, VideoFrame*>* buffer_ = nullptr;
    XVInsertFrame::shared insertFrame_ = nullptr;
    int capacity_ = 0;
    int64_t last_video_pts_in_buf_ = -1; //(buffer) video frame
    int64_t last_video_pts_ = -1;        //insert frame
    int step_pts = -1;
    bool force_key_frame_ = false;
    bool ready_insert_ = false;
    bool insert_frame_time_ = false;

    Listener outfunc_;

public:
    XVBuffer();
    ~XVBuffer();

    void setListener(const Listener& func);

    int init(int buffer_size, bool has_video, int width
                , int height, int bit_rate
                , int fps, mediacodec_id_t codec);
    int add(uint8_t* data , size_t len
            , const int64_t& pts, MediaFrame::Type media_type
            , bool is_key_frame);

    int flush();

    bool opened();


private:
    void checkInsertFrame(std::vector<MediaFrame*>* frames, const int64_t& pts);
    void checkout();
    void checkBuffer();
};


#endif //__XVIDEO_UTIL_HPP__